#ifndef PW_PBL_META_STRING_HPP
#define PW_PBL_META_STRING_HPP

#include <algorithm>
#include <cstddef>
#include <string>
#include "../Throw.hpp"

namespace pw_pbl::Meta
{
	template <typename CHAR, size_t... N> requires requires { std::char_traits<CHAR>{}; } && (sizeof...(N) <= 1)
	struct Str;

	template <typename CHAR> requires requires { std::char_traits<CHAR>{}; }
	struct Str<CHAR>
	{
		inline auto *data() noexcept { return reinterpret_cast<CHAR *>(this); }
		inline auto *data() const noexcept { return reinterpret_cast<const CHAR *>(this); }
		bool operator==(const Str &) const = default;

	protected:
		Str() = default;
		Str(Str &&) = default;
		Str(const Str &) = default;
	};

	template <typename CHAR, size_t N> requires requires { std::char_traits<CHAR>{}; }
	struct Str<CHAR, N> : public Str<CHAR>
	{
		using char_type = CHAR;
		static constexpr size_t ESize = N - 1;

		char_type _data[N];

		constexpr Str(const char_type (&_data)[N]) { pw_pbl_assert(_data[ESize] == 0) std::copy_n(_data, N, this->_data); }

		bool operator==(const Str &) const = default;
		template <typename NC, size_t NN>
		constexpr bool operator==(const Str<NC, NN> &) const { return false; }

		constexpr auto &cstr() { return _data; }
		constexpr auto &data() { return _data; }
		constexpr auto *begin() noexcept { return &_data[0]; }
		constexpr auto *end() noexcept { return &_data[ESize]; }
		constexpr auto &cstr() const { return _data; }
		constexpr auto &data() const { return _data; }
		constexpr auto *begin() const noexcept { return &_data[0]; }
		constexpr auto *end() const noexcept { return &_data[ESize]; }
		constexpr size_t size() const noexcept { return ESize; }
		constexpr std::basic_string_view<CHAR> view() const { return {_data, ESize}; }
		constexpr operator std::basic_string_view<CHAR>() const { return {_data, ESize}; }
	};
	template <typename CHAR, size_t N>
	Str(const CHAR (&)[N]) -> Str<CHAR, N>;

	template <typename CHAR = const char>
	struct StrRef
	{
		static constexpr auto EIsConst = std::is_const_v<CHAR>;
		using TStr = std::conditional_t<EIsConst, const Str<std::remove_const_t<CHAR>>, Str<CHAR>>;

		union
		{
			TStr *pStr;
			std::conditional_t<EIsConst, const void, void> *pVoid;
		};

		constexpr StrRef(TStr &raw) noexcept : pStr{&raw} {}

		template <typename TRAITS>
		constexpr operator std::basic_string_view<std::remove_const_t<CHAR>, TRAITS>() const noexcept { return {pStr->data()}; }

		constexpr auto *data() const noexcept { return pStr->data(); }
	};
	template <typename CHAR>
	StrRef(Str<CHAR> &) -> StrRef<CHAR>;
	template <typename CHAR>
	StrRef(const Str<CHAR> &) -> StrRef<const CHAR>;

	inline namespace literals
	{
		template <Str STR>
		constexpr auto &operator""_meta() { return STR; }

		template <Str... STR>
		constexpr auto MetaConcat = [] { return; }();

		template <typename CHAR, size_t... N, Str<CHAR, N>... STR>
		constexpr auto MetaConcat<STR...> = [] {
			constexpr size_t cnt = sizeof...(STR);
			constexpr size_t lens[cnt] = {(N - 1)...};
			constexpr const CHAR *str[cnt] = {STR.data()...};
			constexpr auto total_len = (N + ... + 0) - cnt;
			CHAR buf[total_len + 1];
			for (size_t i = 0, total = 0; i < cnt; total += lens[i++])
				std::copy_n(str[i], lens[i], &buf[total]);
			buf[total_len] = CHAR{};
			return Str<CHAR, total_len + 1>{buf};
		}();
	} // namespace literals

	template <typename CHAR = char>
	struct StaticStr
	{
		const CHAR (&ref)[];

		template <size_t N>
		consteval StaticStr(const CHAR (&str)[N]) : ref(str) {}
		constexpr StaticStr(const StaticStr &oth) : ref(oth.ref) {}
		constexpr void operator=(const StaticStr &oth) { std::construct_at(this, oth); }

		constexpr std::basic_string_view<CHAR> view() const { return {ref}; }
		constexpr operator const CHAR *() const { return ref; }

		template <typename TRAITS>
		constexpr operator std::basic_string_view<CHAR, TRAITS>() const { return {ref}; }
	};

} // namespace pw_pbl::Meta

#endif // PW_PBL_METASTRING_HPP
